#include <fstream>
#include <string>
#include <iostream>
#include <sstream>
#include <stdlib.h>

using namespace std;

struct ParsedData
{
    float totalEnergy;
    
    ParsedData() : totalEnergy(0)
    {}
};

class Parser
{
    ifstream 	_f;
    ParsedData* _pd;
    string	_line;
    size_t	_n1;
    size_t	_n2;
    
    void _openFile(const char* filename);
    void _findStart(size_t structNum);
    bool _isEndLine() const;
    bool _nucInRange(size_t nuc) const;
    void _processLine();
    void _readNextLine();
    template <class T> inline void _getDataAt(string::size_type pos, T& data) const;
    
public:
    void parse(const char* filename, size_t structNum, size_t n1, size_t n2, ParsedData& data);
};

struct CmdLineData
{
    const char* filename;
    size_t structN;
    size_t n1;
    size_t n2;
};

void readCmdLine(int argc, char* argv[], CmdLineData& cmdData)
{
    if (argc == 5)
    {
	cmdData.filename = argv[1];
	cmdData.structN  = atol(argv[2]);
	cmdData.n1       = atol(argv[3]);
	cmdData.n2       = atol(argv[4]);
	
	if (cmdData.n1 > cmdData.n2 && cmdData.n2 != 0)
	    throw "Invalid initial and ending nucleotides. They should be: n1 > n2, or n2 = 0.";
    }
    else
    {
	throw "Invalid command line arguments. Should be: FILE structN n1 n2";
    }
}

int main(int argc, char* argv[])
{
    ParsedData pd;
    Parser p;
    CmdLineData cmdData;    
    
    try
    {
	readCmdLine(argc, argv, cmdData);
	p.parse(cmdData.filename, cmdData.structN, cmdData.n1, cmdData.n2, pd);
	cout << "Total energy: " << pd.totalEnergy << "\n";
    
    }
    catch(const char* err)
    {
	cerr << "Error: " << err << "\n";
    }
    catch(...)
    {
	cerr << "Unknown error\n";
    }
}

// ------------------------------------
template <class T> 
inline void Parser::_getDataAt(string::size_type pos, T& data) const
{
    stringstream ss(_line.substr(pos));
    ss >> data;
}

void Parser::parse(const char* filename, size_t structNum, size_t n1, size_t n2, ParsedData& data)
{    
    _pd = &data;
    _n1 = n1;
    _n2 = n2;
    
    _openFile(filename);
    
    _findStart(structNum);

    while(!_isEndLine())
    {
	_processLine();
	_readNextLine();
    }    
    
}

void Parser::_openFile(const char* filename)
{
    // encender los flags de excepciones, como mierda se hacia??    
    _f.open(filename);
    if (_f.bad() || _f.eof())
	throw "File not found";
}

void Parser::_findStart(size_t structNum)
{
    bool found (false);
    stringstream ss;
    string structureWord;
    size_t structFound (0);
            
    do
    {
	_readNextLine();
	ss.clear();
	ss.str(_line);
	ss >> structureWord;
	if (structureWord == "Structure")
	{
	    ss >> structFound;
	    found = (structFound == structNum);
	}
    }
    while(!found);
    
    _readNextLine();
}

bool Parser::_isEndLine() const
{
    return (_line.find("Structure ") == 0);
}

void Parser::_processLine()
{
    const string::size_type nucParents = _line.find("(");
    
    if (nucParents != string::npos)
    {
	// filter by nucleotide (in range)
	size_t nuc = 0;
	_getDataAt(nucParents+1, nuc);
	if (_nucInRange(nuc))
	{
	    // find 'ddG' word:
	    string::size_type ddGpos = _line.find("ddG =");
	    if (ddGpos != string::npos)
	    {
		float energy(0);
		_getDataAt(ddGpos + sizeof("ddG ="), energy);
		_pd->totalEnergy += energy;
	    }
	    else
		throw "Unknown format";
	}
    }
}

void Parser::_readNextLine()
{
    getline(_f, _line);
    if (_f.eof() || _f.bad())
	throw "Unexpected end of line";
}

bool Parser::_nucInRange(size_t nuc) const
{
    return (_n1 <= nuc && (_n2 == 0 || nuc <= _n2));
}


